<template>
    <div
        ref="button"
        class="zx-slider__button-wrapper"
        :class="{ 'hover': hovering, 'dragging': dragging }"
        :style="wrapperStyle"
        tabindex="0"
        @mouseenter="handleMouseEnter"
        @mouseleave="handleMouseLeave"
        @mousedown="onButtonDown"
        @touchstart="onButtonDown"
        @focus="handleMouseEnter"
        @blur="handleMouseLeave"
        @keydown.left="onLeftKeyDown"
        @keydown.right="onRightKeyDown"
        @keydown.down.prevent="onLeftKeyDown"
        @keydown.up.prevent="onRightKeyDown"
    >
        <ZxTooltip
            ref="tooltip"
            placement="top"
            :popper-class="tooltipClass"
            :disabled="!showTooltip"
        >
            <span slot="content">{{ formatValue }}</span>
            <div class="zx-slider__button" :class="{ 'hover': hovering, 'dragging': dragging }" />
        </ZxTooltip>
    </div>
</template>

<script>
import ZxTooltip from '../../../components/tooltip';

export default {
    name: 'ZxSliderButton',

    components: {
        ZxTooltip,
    },

    props: {
        value: {
            type: Number,
            default: 0,
        },
        vertical: {
            type: Boolean,
            default: false,
        },
        tooltipClass: String,
    },

    data() {
        return {
            hovering: false,
            dragging: false,
            isClick: false,
            startX: 0,
            currentX: 0,
            startY: 0,
            currentY: 0,
            startPosition: 0,
            newPosition: null,
            oldValue: this.value,
        };
    },

    computed: {
        disabled() {
            return this.$parent.sliderDisabled;
        },

        max() {
            return this.$parent.max;
        },

        min() {
            return this.$parent.min;
        },

        step() {
            return this.$parent.step;
        },

        showTooltip() {
            return this.$parent.showTooltip;
        },

        precision() {
            return this.$parent.precision;
        },

        currentPosition() {
            return `${(this.value - this.min) / (this.max - this.min) * 100}%`;
        },

        enableFormat() {
            return this.$parent.formatTooltip instanceof Function;
        },

        formatValue() {
            return this.enableFormat && this.$parent.formatTooltip(this.value) || this.value;
        },

        wrapperStyle() {
            return this.vertical ? { bottom: this.currentPosition } : { left: this.currentPosition };
        },
    },

    watch: {
        dragging(val) {
            this.$parent.dragging = val;
        },
    },

    methods: {
        displayTooltip() {
            this.$refs.tooltip && (this.$refs.tooltip.showPopper = true);
        },

        hideTooltip() {
            this.$refs.tooltip && (this.$refs.tooltip.showPopper = false);
        },

        handleMouseEnter() {
            this.hovering = true;
            this.displayTooltip();
        },

        handleMouseLeave() {
            this.hovering = false;
            this.hideTooltip();
        },

        onButtonDown(event) {
            if (this.disabled) return;
            event.preventDefault();
            this.onDragStart(event);
            window.addEventListener('mousemove', this.onDragging);
            window.addEventListener('touchmove', this.onDragging);
            window.addEventListener('mouseup', this.onDragEnd);
            window.addEventListener('touchend', this.onDragEnd);
            window.addEventListener('contextmenu', this.onDragEnd);
        },
        onLeftKeyDown() {
            if (this.disabled) return;
            this.newPosition = parseFloat(this.currentPosition) - this.step / (this.max - this.min) * 100;
            this.setPosition(this.newPosition);
            this.$parent.emitChange();
        },
        onRightKeyDown() {
            if (this.disabled) return;
            this.newPosition = parseFloat(this.currentPosition) + this.step / (this.max - this.min) * 100;
            this.setPosition(this.newPosition);
            this.$parent.emitChange();
        },
        onDragStart(event) {
            this.dragging = true;
            this.isClick = true;
            if (event.type === 'touchstart') {
                event.clientY = event.touches[0].clientY;
                event.clientX = event.touches[0].clientX;
            }
            if (this.vertical) {
                this.startY = event.clientY;
            } else {
                this.startX = event.clientX;
            }
            this.startPosition = parseFloat(this.currentPosition);
            this.newPosition = this.startPosition;
        },

        onDragging(event) {
            if (this.dragging) {
                this.isClick = false;
                this.displayTooltip();
                this.$parent.resetSize();
                let diff = 0;
                if (event.type === 'touchmove') {
                    event.clientY = event.touches[0].clientY;
                    event.clientX = event.touches[0].clientX;
                }
                if (this.vertical) {
                    this.currentY = event.clientY;
                    diff = (this.startY - this.currentY) / this.$parent.sliderSize * 100;
                } else {
                    this.currentX = event.clientX;
                    diff = (this.currentX - this.startX) / this.$parent.sliderSize * 100;
                }
                this.newPosition = this.startPosition + diff;
                this.setPosition(this.newPosition);
            }
        },

        onDragEnd() {
            if (this.dragging) {
                /*
                 * 防止在 mouseup 后立即触发 click，导致滑块有几率产生一小段位移
                 * 不使用 preventDefault 是因为 mouseup 和 click 没有注册在同一个 DOM 上
                 */
                setTimeout(() => {
                    this.dragging = false;
                    this.hideTooltip();
                    if (!this.isClick) {
                        this.setPosition(this.newPosition);
                        this.$parent.emitChange();
                    }
                }, 0);
                window.removeEventListener('mousemove', this.onDragging);
                window.removeEventListener('touchmove', this.onDragging);
                window.removeEventListener('mouseup', this.onDragEnd);
                window.removeEventListener('touchend', this.onDragEnd);
                window.removeEventListener('contextmenu', this.onDragEnd);
            }
        },

        setPosition(newPosition) {
            if (newPosition === null || isNaN(newPosition)) return;
            if (newPosition < 0) {
                newPosition = 0;
            } else if (newPosition > 100) {
                newPosition = 100;
            }
            const lengthPerStep = 100 / ((this.max - this.min) / this.step);
            const steps = Math.round(newPosition / lengthPerStep);
            let value = steps * lengthPerStep * (this.max - this.min) * 0.01 + this.min;
            value = parseFloat(value.toFixed(this.precision));
            this.$emit('input', value);
            this.$nextTick(() => {
                this.displayTooltip();
                this.$refs.tooltip && this.$refs.tooltip.updatePopper();
            });
            if (!this.dragging && this.value !== this.oldValue) {
                this.oldValue = this.value;
            }
        },
    },
};
</script>
